from typing import Optional, List

import uuid
import time
import json
import hashlib
import secrets
import base64
from datetime import datetime

STANDARD_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
CUSTOM_BASE64_CHARS = "GHp14VWYCg9FUjJL5K60Ssr23RMuEzleaO8hAbwyPQxqmZcINDtXfv7oTnikBd!$"

encode_trans = str.maketrans(STANDARD_BASE64_CHARS, CUSTOM_BASE64_CHARS)
decode_trans = str.maketrans(CUSTOM_BASE64_CHARS, STANDARD_BASE64_CHARS)


def generate_unique_id(length: int = 12, optional_params: dict = None, use_timestamp: bool = True) -> str:
    """
    生成一个基于 UUID 和时间戳的唯一 ID，支持可选参数和指定长度。

    :param length: 生成的唯一 ID 的长度（字符数）。默认为 12。
    :param optional_params: 可选参数，字典类型，会被序列化并加入唯一 ID 的生成中。
    :param use_timestamp: 是否在生成 ID 时加入时间戳。默认为 True。
    :return: 生成的唯一 ID，字符串类型。
    """
    # 基础数据：UUID 和时间戳
    base_data = str(uuid.uuid4())  # 使用 UUID 作为基础唯一性来源
    if use_timestamp:
        timestamp = str(int(time.time() * 1000))  # 毫秒级时间戳
        base_data += timestamp

    # 如果有可选参数，将其序列化并加入基础数据
    if optional_params:
        import json
        params_str = json.dumps(optional_params, sort_keys=True)  # 确保参数顺序一致
        base_data += params_str

    # 使用 SHA-256 哈希算法生成固定长度的哈希值
    hash_object = hashlib.sha256(base_data.encode('utf-8'))
    hex_dig = hash_object.hexdigest()  # 获取哈希值的十六进制表示

    # 截取指定长度的唯一 ID
    unique_id = hex_dig[:length]

    return unique_id


def custom_b64encode(data: bytes) -> str:
    standard_b64 = base64.b64encode(data).decode()
    return standard_b64.translate(encode_trans)


def custom_b64decode(custom_b64: str) -> bytes:
    standard_b64 = custom_b64.translate(decode_trans)
    return base64.b64decode(standard_b64)


class AppFlagKey(dict):
    def __init__(self, flag: str, key: str, version: int):
        self.flag = flag
        self.key = key
        self.version = version
        super().__init__({
            'flag': flag,
            'key': key,
            'fk_version': version,
        })


def generate_app_fk(account_id: str):
    """基于账号 ID 与 FK 版本生成 flag 与 key"""
    app_fk_version = int(datetime.now().timestamp())
    data = f'x1={account_id}; x2={app_fk_version}'
    md5 = hashlib.md5(data.encode()).hexdigest()
    b64 = custom_b64encode(md5.encode())
    equal_count = b64.count('=')
    b64 = f"{equal_count}{b64.replace('=', '')}"
    return AppFlagKey(b64[:20], b64[20:], app_fk_version)


def verify_app_fk(account_id: str, app_fk_version: int, flag: str, key: str):
    try:
        data = f'x1={account_id}; x2={app_fk_version}'
        raw_md5 = hashlib.md5(data.encode()).hexdigest()
        raw_b64 = f'{flag}{key}'
        b64 = raw_b64[1:] + '=' * int(raw_b64[0])
        fk_md5 = custom_b64decode(b64)
        return secrets.compare_digest(raw_md5.encode(), fk_md5)
    except:
        return False


def generate_session_id(account_id: str, login_time: int, expire_seconds: int):
    unique_id = generate_unique_id(16, {
        "aid": account_id,
        "login_time": login_time,
        "expire_seconds": expire_seconds
    })
    raw_session_id = custom_b64encode(unique_id.encode())
    equal_count = raw_session_id.count('=')
    non_equal_text = raw_session_id.replace('=', '')
    return f'{equal_count}{non_equal_text}'


def generate_token(
        data: Optional[dict] = None,
        secret_key: Optional[str] = None,
        hash_algorithm: str = "sha256"
) -> str:
    """生成高安全性随机Token，可结合输入数据生成唯一Token"""
    random_part = secrets.token_urlsafe(32)[:32]

    if data:
        sorted_data = json.dumps(data, sort_keys=True)
        if secret_key:
            sorted_data += secret_key

        hasher = hashlib.new(hash_algorithm)
        hasher.update(sorted_data.encode('utf-8'))
        data_hash = hasher.hexdigest()
        combined = (random_part + data_hash)[:32]
        return combined

    return random_part


def generate_random_chars(count: int, length: int, rule: str, prefix: str, mixin_weight: int = 1) -> List[str]:
    """
    根据指定规则，生成指定数量指定长度带有前缀的随机字符串列表
    :param count: 需要生成的数量
    :param length: 除前缀外的长度
    :param rule: 字符规则，比如 abcdefg... 等等，至少 32 位，最高 128 位
    :param prefix: 最终前缀
    :param mixin_weight: 混淆权重
    :return:
    """
    unique_rule = "".join(dict.fromkeys(rule))
    result = []

    mixin_count = max(1, (mixin_weight + length) // 100)

    for _ in range(count):
        random_chars = [secrets.choice(unique_rule) for _ in range(length)]
        for _ in range(mixin_count):
            pos = secrets.randbelow(length)
            random_chars[pos] = secrets.choice(unique_rule)

        random_part = ''.join(random_chars)
        result.append(f'{prefix}{random_part}')

    return result
